#include <algorithm>
#include <iostream>
#include <vector>
#include <string>

using namespace std;

#include "luals.h"
#include <luabind/luabind.hpp>
#include "../luabindSensor/LuabindSensor.h"

extern "C" {

static char * getVersion( )
{
    static char marker[] = { "luabindSensor v" LIBVER " (c) All rights reserved."};
    return( marker );
}

static int pusherror(lua_State *L, const char *info)
{
    lua_pushstring(L, info);

    return 1;
}


static int luabindSensor_members(lua_State* L)
{    
    luabind::object o(luabind::from_stack(L, 1));
    if (!o.is_valid())
        return pusherror(L, "object is not valid");

    if (!lua_istable(L, 1) && !lua_isuserdata(L, 1))        
    {
        lua_pop(L, 1);
        return pusherror(L, "table or userdata only");
    }

    if (lua_istable(L, 1))
    {
        LuabindSensor::TableInfo::SharePtr info = LuabindSensor::CreateTableDetail("", o);
        lua_pop(L, 1);

        lua_newtable (L);

        for(LuabindSensor::TableInfo::List::const_iterator i=info->subTables.begin(),end=info->subTables.end();i!=end;i++)
        {
            lua_pushstring(L, (*i)->name.c_str());
            (*i)->obj.push(L);
            lua_rawset(L, -3);
        }

        for(LuabindSensor::ClassInfo::List::const_iterator i=info->classes.begin(),end=info->classes.end();i!=end;i++)
        {
            lua_pushstring(L, (*i)->name.c_str());
            (*i)->obj.push(L);
            lua_rawset(L, -3);
        }

        for(LuabindSensor::FunctionInfo::List::const_iterator i=info->functions.begin(),end=info->functions.end();i!=end;i++)
        {
            lua_pushstring(L, (*i)->name.c_str());
            (*i)->obj.push(L);
            lua_rawset(L, -3);
        }

        for(LuabindSensor::ObjectInfo::List::const_iterator i=info->others.begin(),end=info->others.end();i!=end;i++)
        {
            lua_pushstring(L, (*i)->name.c_str());
            (*i)->obj.push(L);
            lua_rawset(L, -3);
        }
    }
    else
    {
        if (LuabindSensor::IsClassDefinition(o) )
        {
            LuabindSensor::ClassInfo::SharePtr info = LuabindSensor::CreateClassDetail("", o) ;
            lua_pop(L, 1);

            lua_newtable (L);

            for(LuabindSensor::ClassInfo::EnumList::const_iterator i=info->enums.begin(),end=info->enums.end();i!=end;i++)
            {
                lua_pushstring(L, i->first.c_str());
                lua_pushnumber(L, i->second);
                lua_rawset(L, -3);
            }

            for(LuabindSensor::PropertyInfo::List::const_iterator i=info->properties.begin(),end=info->properties.end();i!=end;i++)
            {
                lua_pushstring(L, (*i)->name.c_str());
                (*i)->obj.push(L);
                lua_rawset(L, -3);
            }

            for(LuabindSensor::FunctionInfo::List::const_iterator i=info->methods.begin(),end=info->methods.end();i!=end;i++)
            {
                lua_pushstring(L, (*i)->name.c_str());
                (*i)->obj.push(L);
                lua_rawset(L, -3);
            }

            for(LuabindSensor::FunctionInfo::List::const_iterator i=info->staticMethods.begin(),end=info->staticMethods.end();i!=end;i++)
            {
                lua_pushstring(L, (*i)->name.c_str());
                (*i)->obj.push(L);
                lua_rawset(L, -3);
            }
        } 
        else
        {
            LuabindSensor::ObjectInfo::SharePtr info = LuabindSensor::CreateObjectDetail("", o);
            lua_pop(L, 1);

            lua_newtable (L);
            lua_pushstring(L, info->name.c_str());
            info->obj.push(L);
            lua_rawset(L, -3);
        }         
    }
    return 1;
}

static const luaL_reg R[] =
{
    {"members", luabindSensor_members},
    {NULL,NULL}
};

/**
 * Bind our exported functions to the Lua intepretter, making our functions
 * available to the calling script.
 * @param L The lua intepreter object.
 * @return 1 (for the table we leave on the stack)
 */

LUALIB_API int luaopen_luals (lua_State *L)
{

#ifdef PRE_LUA51
    luaL_openlib (L, LIBNAME, R, 0);
#else
    luaL_register (L, LIBNAME, R);
#endif

    char *version = getVersion();
    lua_pushliteral (L, "_VERSION");
    lua_pushstring (L, version );
    lua_settable (L, -3);

    return 1;
}


};
